package wdstruct

import (
	"runtime"
	"sync/atomic"
	"time"
)

type GrtnPoolType string

const (
	AsyncPool GrtnPoolType = "AsyncPool"
	DealPool GrtnPoolType = "BlockPool"
)

type GrtnPool struct {
	MaxCount int32
	NowCount int32
	FreeCount int32
	PoolType GrtnPoolType
	taskchan chan func()
	State int32
}

var default_gp = GrtnPool{
	MaxCount:  9999,
	FreeCount: 8,
	PoolType:  AsyncPool,
	taskchan:  make(chan func(),9999),
	State:     1,
}

type GrtnPoolOption  func (g*GrtnPool) *GrtnPool

func NewGrtnPool(args ...GrtnPoolOption) *GrtnPool{
	gp := default_gp
	gpptr := &gp
	for _,arg := range args  {
		gpptr = arg(gpptr)
	}
	gpptr.Run()
	return gpptr
}

func (g*GrtnPool)Run(){
	switch g.PoolType {
	case AsyncPool:
		for i:=0;i<int(g.FreeCount);i++ {
			 g.WorkerHandler()
		}
		go func(){
			for atomic.LoadInt32(&(g.State)) != 0 {
				if len(g.taskchan) > 0 {
					if atomic.LoadInt32(&(g.NowCount)) < g.MaxCount {
						 g.WorkerHandler()
						continue
					}
				}else{
					if atomic.LoadInt32(&(g.State)) != 1{
						return
					}
				}
				<-time.After(time.Second)
			}
		}()
	case DealPool:
		cpus := runtime.NumCPU()
		g.FreeCount = int32(cpus+1)
		g.MaxCount = g.FreeCount
		for i:=0;i<cpus;i++ {
			go g.WorkerHandler()
		}
	default:
		panic("PoolType not found")
	}

}
func (g *GrtnPool)GetGoroutineCount()int32{
	return atomic.LoadInt32(&(g.NowCount))
}

func (g *GrtnPool)GetTaskCount()int{
	return len(g.taskchan)
}

func (g*GrtnPool)Submit(f func()){
	if atomic.LoadInt32(&(g.State)) !=1 {
		return
	}
	if f == nil{
		return
	}
	g.taskchan<-f
}
func (g*GrtnPool)TryClose(){
	atomic.SwapInt32(&(g.State),2)
}
func (g*GrtnPool)ForcedClose(){
	atomic.SwapInt32(&(g.State),0)
	close(g.taskchan)
}
func (g *GrtnPool)WorkerHandler(){
	atomic.AddInt32(&(g.NowCount),1)
	go func(){
		defer atomic.AddInt32(&(g.NowCount),-1)
		for atomic.LoadInt32(&(g.State)) != 0 {
			select {
			case task,ok := <- g.taskchan:
				if !ok {
					return
				}
				if task!=nil{
					task()
				}else{
					return
				}
			default:
				if atomic.LoadInt32(&(g.State)) !=1 {
					return
				}
				if atomic.LoadInt32(&(g.NowCount)) > g.FreeCount{
					return
				}
			}
		}
	}()
}


func SetGrtnMax(max int32)GrtnPoolOption{
	return func(g *GrtnPool) *GrtnPool {
		g.MaxCount = max
		return g
	}
}
func SetGrtnFree(free int32)GrtnPoolOption{
	return func(g *GrtnPool) *GrtnPool {
		g.FreeCount = free
		return g
	}
}
func SetGrtnTaskLen(len int)GrtnPoolOption{
	return func(g *GrtnPool) *GrtnPool {
		g.taskchan = make(chan func(),len)
		return g
	}
}
func SetGrtnPoolType(t GrtnPoolType)GrtnPoolOption{
	return func(g *GrtnPool) *GrtnPool {
		g.PoolType = t
		return g
	}
}